import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
import { API_KEY, SECRET_KEY } from '../../config/config';

interface TokenData {
  access_token: string;
  expires_in: number;
  expiresDate: number;
}

@Injectable()
export class BaiduToken {
  private tokenData: TokenData | null = null;
  constructor(private httpService: HttpService) {}

  /**
   * 异步获取访问令牌
   * 本函数不接受任何参数。
   * @returns {Promise<TokenData>} 返回一个承诺（Promise），该承诺解析为TokenData类型，包含访问令牌信息。
   */
  async getAccessToken(): Promise<TokenData> {
    // 准备请求参数
    const params = {
      grant_type: 'client_credentials',
      client_id: API_KEY,
      client_secret: SECRET_KEY,
    };

    // 设置请求的URL
    const url = 'https://aip.baidubce.com/oauth/2.0/token';

    try {
      // 发起POST请求获取访问令牌，并处理响应
      const response = await firstValueFrom(
        this.httpService.post(url, null, {
          headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
          },
          params: params,
        })
      );
      // 返回响应数据
      return response.data;
    } catch (error) {
      // 打印错误并抛出获取token失败的错误
      console.error(error);
      throw new Error('获取token失败');
    }
  }

  /**
   * 异步获取Token数据。
   * 该函数首先检查当前Token是否有效，如果无效则通过调用`getAccessToken`来获取新的Token数据，
   * 并更新Token的过期时间。最后返回Token数据。
   * 
   * @returns {Promise<TokenData>} 返回一个Promise，解析为TokenData类型的对象。
   */
  async getToken(): Promise<TokenData> {
    // 检查当前Token是否有效
    if (!this.checkTokenEffect()) {
      // 如果无效，调用getAccessToken获取新的Token数据
      let data = await this.getAccessToken();
      // 如果获取的数据中没有access_token，抛出错误
      if (!data?.access_token) throw new Error('token缺乏数据');
      // 更新Token的过期时间
      data.expiresDate = Date.now() + data.expires_in * 1000;
      this.setToken(data);
    }
    // 返回Token数据
    return this.tokenData!;
  }

   // 设置token，简单设置下，后续添加数据库逻辑再调整
  setToken(data: TokenData): void {
    this.tokenData = data;
  }

  // 检查token是否有效
  checkTokenEffect(): boolean {
    return this.tokenData !== null && this.tokenData.expiresDate! >= Date.now();
  }

  // 重置token，简单重置下，后续添加数据库逻辑再调整
  resetToken(): void {
    this.tokenData = null;
  }
}
